home *** CD-ROM | disk | FTP | other *** search
- // VirtualDub - Video processing and capture application
- // Copyright (C) 1998-2001 Avery Lee
- //
- // This program is free software; you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation; either version 2 of the License, or
- // (at your option) any later version.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with this program; if not, write to the Free Software
- // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- #include <crtdbg.h>
-
- #include <windows.h>
-
- #include "AVIReadHandler.h"
- #include "FastReadStream.h"
- #include "ProgressDialog.h"
- #include "AVIIndex.h"
- #include "Error.h"
- #include "List.h"
- #include "Fixes.h"
- #include "File64.h"
- #include "Avisynth.h"
-
- //#define STREAMING_DEBUG
-
- // HACK!!!!
-
- CRITICAL_SECTION g_diskcs;
- bool g_disklockinited=false;
-
-
- ///////////////////////////////////////////
-
- typedef __int64 QUADWORD;
-
- // The following comes from the OpenDML 1.0 spec for extended AVI files
-
- // bIndexType codes
- //
- #define AVI_INDEX_OF_INDEXES 0x00 // when each entry in aIndex
- // array points to an index chunk
-
- #define AVI_INDEX_OF_CHUNKS 0x01 // when each entry in aIndex
- // array points to a chunk in the file
-
- #define AVI_INDEX_IS_DATA 0x80 // when each entry is aIndex is
- // really the data
-
- // bIndexSubtype codes for INDEX_OF_CHUNKS
-
- #define AVI_INDEX_2FIELD 0x01 // when fields within frames
- // are also indexed
- struct _avisuperindex_entry {
- QUADWORD qwOffset; // absolute file offset, offset 0 is
- // unused entry??
- DWORD dwSize; // size of index chunk at this offset
- DWORD dwDuration; // time span in stream ticks
- };
- struct _avistdindex_entry {
- DWORD dwOffset; // qwBaseOffset + this is absolute file offset
- DWORD dwSize; // bit 31 is set if this is NOT a keyframe
- };
- struct _avifieldindex_entry {
- DWORD dwOffset;
- DWORD dwSize;
- DWORD dwOffsetField2;
- };
-
- #pragma pack(push)
- #pragma pack(2)
-
- typedef struct _avisuperindex_chunk {
- FOURCC fcc; // Æix##Æ
- DWORD cb; // size of this structure
- WORD wLongsPerEntry; // must be 4 (size of each entry in aIndex array)
- BYTE bIndexSubType; // must be 0 or AVI_INDEX_2FIELD
- BYTE bIndexType; // must be AVI_INDEX_OF_INDEXES
- DWORD nEntriesInUse; // number of entries in aIndex array that
- // are used
- DWORD dwChunkId; // Æ##dcÆ or Æ##dbÆ or Æ##wbÆ, etc
- DWORD dwReserved[3]; // must be 0
- struct _avisuperindex_entry aIndex[];
- } AVISUPERINDEX, * PAVISUPERINDEX;
-
- typedef struct _avistdindex_chunk {
- FOURCC fcc; // Æix##Æ
- DWORD cb;
- WORD wLongsPerEntry; // must be sizeof(aIndex[0])/sizeof(DWORD)
- BYTE bIndexSubType; // must be 0
- BYTE bIndexType; // must be AVI_INDEX_OF_CHUNKS
- DWORD nEntriesInUse; //
- DWORD dwChunkId; // Æ##dcÆ or Æ##dbÆ or Æ##wbÆ etc..
- QUADWORD qwBaseOffset; // all dwOffsets in aIndex array are
- // relative to this
- DWORD dwReserved3; // must be 0
- struct _avistdindex_entry aIndex[];
- } AVISTDINDEX, * PAVISTDINDEX;
-
- typedef struct _avifieldindex_chunk {
- FOURCC fcc;
- DWORD cb;
- WORD wLongsPerEntry;
- BYTE bIndexSubType;
- BYTE bIndexType;
- DWORD nEntriesInUse;
- DWORD dwChunkId;
- QUADWORD qwBaseOffset;
- DWORD dwReserved3;
- struct _avifieldindex_entry aIndex[];
- } AVIFIELDINDEX, * PAVIFIELDINDEX;
-
- #pragma pack(pop)
-
- ///////////////////////////////////////////////////////////////////////////
-
- IAVIReadStream::~IAVIReadStream() {
- }
-
- ///////////////////////////////////////////////////////////////////////////
-
- class AVIStreamNode;
- class AVIReadHandler;
-
- class AVIReadCache {
- public:
- long cache_hit_bytes, cache_miss_bytes;
- int reads;
-
- AVIReadCache(int nlines, int nstream, AVIReadHandler *root, AVIStreamNode *psnData);
- ~AVIReadCache();
-
- void ResetStatistics();
- bool WriteBegin(__int64 pos, long len);
- void Write(void *buffer, long len);
- void WriteEnd();
- long Read(void *dest, __int64 chunk_pos, __int64 pos, long len);
-
- private:
- AVIStreamNode *psnData;
- __int64 (*buffer)[2];
- int lines_max, lines;
- long read_tail, write_tail, write_hdr;
- int write_offset;
- int stream;
- AVIReadHandler *source;
- };
-
- class AVIStreamNode : public ListNode2<AVIStreamNode> {
- public:
- AVIStreamHeader_fixed hdr;
- void *pFormat;
- long lFormatLen;
- AVIIndex index;
- __int64 bytes;
- bool keyframe_only;
- int handler_count;
- class AVIReadCache *cache;
- int streaming_count;
- __int64 stream_push_pos;
- __int64 stream_bytes;
- int stream_pushes;
- long length;
- long frames;
- List2<class AVIReadStream> listHandlers;
-
- AVIStreamNode();
- ~AVIStreamNode();
- };
-
- AVIStreamNode::AVIStreamNode() {
- pFormat = NULL;
- bytes = 0;
- handler_count = 0;
- streaming_count = 0;
-
- stream_bytes = 0;
- stream_pushes = 0;
- cache = NULL;
- }
-
- AVIStreamNode::~AVIStreamNode() {
- delete pFormat;
- delete cache;
- }
-
- ///////////////////////////////////////////////////////////////////////////
-
- class AVIFileDesc : public ListNode2<AVIFileDesc> {
- public:
- HANDLE hFile;
- HANDLE hFileUnbuffered;
- __int64 i64Size;
- };
-
- class AVIStreamNode;
-
- class AVIReadHandler : public IAVIReadHandler, private File64 {
- public:
- bool fDisableFastIO;
-
- AVIReadHandler(const char *);
- AVIReadHandler(PAVIFILE);
- ~AVIReadHandler();
-
- void AddRef();
- void Release();
- IAVIReadStream *GetStream(DWORD fccType, LONG lParam);
- void EnableFastIO(bool);
- bool isOptimizedForRealtime();
- bool isStreaming();
- bool isIndexFabricated();
- bool AppendFile(const char *pszFile);
- bool getSegmentHint(const char **ppszPath);
-
- void EnableStreaming(int stream);
- void DisableStreaming(int stream);
- void AdjustRealTime(bool fRealTime);
- bool Stream(AVIStreamNode *, _int64 pos);
- __int64 getStreamPtr();
- void FixCacheProblems(class AVIReadStream *);
- long ReadData(int stream, void *buffer, __int64 position, long len);
-
- private:
- // enum { STREAM_SIZE = 65536 };
- enum { STREAM_SIZE = 1048576 };
- enum { STREAM_RT_SIZE = 65536 };
- enum { STREAM_BLOCK_SIZE = 4096 };
-
- IAvisynthClipInfo *pAvisynthClipInfo;
- PAVIFILE paf;
- int ref_count;
- __int64 i64StreamPosition;
- int streams;
- char *streamBuffer;
- int sbPosition;
- int sbSize;
- long fStreamsActive;
- int nRealTime;
- int nActiveStreamers;
- bool fFakeIndex;
- __int64 i64Size;
- int nFiles, nCurrentFile;
- char * pSegmentHint;
-
- List2<AVIStreamNode> listStreams;
- List2<AVIFileDesc> listFiles;
-
- void _construct(const char *pszFile);
- void _parseFile(List2<AVIStreamNode>& streams);
- bool _parseStreamHeader(List2<AVIStreamNode>& streams, DWORD dwLengthLeft);
- bool _parseIndexBlock(List2<AVIStreamNode>& streams, int count, __int64);
- void _parseExtendedIndexBlock(List2<AVIStreamNode>& streams, AVIStreamNode *pasn, __int64 fpos, DWORD dwLength);
- void _destruct();
-
- char * _StreamRead(long& bytes);
- void _SelectFile(int file);
-
- };
-
- IAVIReadHandler *CreateAVIReadHandler(PAVIFILE paf) {
- return new AVIReadHandler(paf);
- }
-
- IAVIReadHandler *CreateAVIReadHandler(const char *pszFile) {
- return new AVIReadHandler(pszFile);
- }
-
- ///////////////////////////////////////////////////////////////////////////
-
- AVIReadCache::AVIReadCache(int nlines, int nstream, AVIReadHandler *root, AVIStreamNode *psnData) {
- buffer = new __int64[nlines][2];
- if (!buffer) throw MyMemoryError();
-
- this->psnData = psnData;
- lines = 0;
- lines_max = nlines;
- read_tail = 0;
- write_tail = 0;
- stream = nstream;
- source = root;
- ResetStatistics();
- }
-
- AVIReadCache::~AVIReadCache() {
- delete[] buffer;
- }
-
- void AVIReadCache::ResetStatistics() {
- reads = 0;
- cache_hit_bytes = cache_miss_bytes = 0;
- }
-
- bool AVIReadCache::WriteBegin(__int64 pos, long len) {
- int needed;
-
- // delete lines as necessary to make room
-
- needed = 1 + (len+15)/16;
-
- if (needed > lines_max)
- return false;
-
- while(lines+needed > lines_max) {
- int siz = (int)((buffer[read_tail][1]+15)/16 + 1);
- read_tail += siz;
- lines -= siz;
- if (read_tail >= lines_max)
- read_tail -= lines_max;
- }
-
- // write in header
-
- // _RPT1(0,"\tbeginning write at line %ld\n", write_tail);
-
- write_hdr = write_tail;
- write_offset = 0;
-
- buffer[write_tail][0] = pos;
- buffer[write_tail][1] = 0;
-
- if (++write_tail >= lines_max)
- write_tail = 0;
-
- return true;
- }
-
- void AVIReadCache::Write(void *src, long len) {
- long dest;
-
- // copy in data
-
- buffer[write_hdr][1] += len;
-
- dest = write_tail + (len + write_offset + 15)/16;
-
- if (dest > lines_max) {
- int tc = (lines_max - write_tail)*16 - write_offset;
-
- memcpy((char *)&buffer[write_tail][0] + write_offset, src, tc);
- memcpy(&buffer[0][0], (char *)src + tc, len - tc);
-
- write_tail = (len-tc)/16;
- write_offset = (len-tc)&15;
-
- } else {
- memcpy((char *)&buffer[write_tail][0] + write_offset, src, len);
- write_tail += (len+write_offset)/16;
- if (write_tail >= lines_max)
- write_tail = 0;
-
- write_offset = (len+write_offset) & 15;
- }
- }
-
- void AVIReadCache::WriteEnd() {
- long cnt = (long)(1 + (buffer[write_hdr][1]+15)/16);
- lines += cnt;
- write_tail = write_hdr + cnt;
-
- if (write_tail >= lines_max)
- write_tail -= lines_max;
-
- // _RPT3(0,"\twrite complete -- header at line %d, size %ld, next line %ld\n", write_hdr, (long)buffer[write_hdr][1], write_tail);
- }
-
- #pragma function(memcpy)
-
- long AVIReadCache::Read(void *dest, __int64 chunk_pos, __int64 pos, long len) {
- long ptr;
- __int64 offset;
-
- // _RPT3(0,"Read request: chunk %16I64, pos %16I64d, %ld bytes\n", chunk_pos, pos, len);
-
- ++reads;
-
- do {
- // scan buffer looking for a range that contains data
-
- ptr = read_tail;
- while(ptr != write_tail) {
- offset = pos - buffer[ptr][0];
-
- // _RPT4(0,"line %8d: pos %16I64d, len %ld bytes (%ld lines)\n", ptr, buffer[ptr][0], (long)buffer[ptr][1], (long)(buffer[ptr][1]+15)/16);
-
- if (offset>=0 && offset < buffer[ptr][1]) {
- long end;
-
- // cache hit
-
- // _RPT1(0, "cache hit (offset %I64d)\n", chunk_pos);
-
- cache_hit_bytes += len;
-
- while (cache_hit_bytes > 16777216) {
- cache_miss_bytes >>= 1;
- cache_hit_bytes >>= 1;
- }
-
- if (len > (long)(buffer[ptr][1]*16 - offset))
- len = (long)(buffer[ptr][1]*16 - offset);
-
- ptr += 1+((long)offset>>4);
- if (ptr >= lines_max)
- ptr -= lines_max;
-
- end = ptr + ((len+((long)offset&15)+15)>>4);
-
- if (end > lines_max) {
- long tc = (lines_max - ptr)*16 - ((long)offset&15);
- memcpy(dest, (char *)&buffer[ptr][0] + ((long)offset&15), tc);
- memcpy((char *)dest + tc, (char *)&buffer[0][0], len-tc);
- } else {
- memcpy(dest, (char *)&buffer[ptr][0] + ((long)offset&15), len);
- }
-
- return len;
- }
-
- // _RPT4(0,"[x] line %8d: pos %16I64d, len %ld bytes (%ld lines)\n", ptr, buffer[ptr][0], (long)buffer[ptr][1], (long)(buffer[ptr][1]+15)/16);
- ptr += (long)(1+(buffer[ptr][1] + 15)/16);
-
- if (ptr >= lines_max)
- ptr -= lines_max;
- }
-
- if (source->getStreamPtr() > chunk_pos)
- break;
-
- } while(source->Stream(psnData, chunk_pos));
-
- // OutputDebugString("cache miss\n");
- // _RPT1(0, "cache miss (offset %I64d)\n", chunk_pos);
-
- cache_miss_bytes += len;
-
- while (cache_miss_bytes > 16777216) {
- cache_miss_bytes >>= 1;
- cache_hit_bytes >>= 1;
- }
-
- return source->ReadData(stream, dest, pos, len);
- }
-
- ///////////////////////////////////////////////////////////////////////////
-
- class AVIReadTunnelStream : public IAVIReadStream {
- public:
- AVIReadTunnelStream(AVIReadHandler *, PAVISTREAM, IAvisynthClipInfo *pClipInfo);
- ~AVIReadTunnelStream();
-
- HRESULT BeginStreaming(long lStart, long lEnd, long lRate);
- HRESULT EndStreaming();
- HRESULT Info(AVISTREAMINFO *pasi, long lSize);
- bool IsKeyFrame(long lFrame);
- HRESULT Read(long lStart, long lSamples, void *lpBuffer, long cbBuffer, long *plBytes, long *plSamples);
- long Start();
- long End();
- long PrevKeyFrame(long lFrame);
- long NextKeyFrame(long lFrame);
- long NearestKeyFrame(long lFrame);
- HRESULT FormatSize(long lFrame, long *plSize);
- HRESULT ReadFormat(long lFrame, void *pFormat, long *plSize);
- bool isStreaming();
- bool isKeyframeOnly();
-
- private:
- IAvisynthClipInfo *const pAvisynthClipInfo;
- AVIReadHandler *const parent;
- const PAVISTREAM pas;
- };
-
- ///////////////////////////////////////////////////////////////////////////
-
- AVIReadTunnelStream::AVIReadTunnelStream(AVIReadHandler *_parent, PAVISTREAM _pas, IAvisynthClipInfo *pClipInfo)
- : pAvisynthClipInfo(pClipInfo)
- , parent(_parent)
- , pas(_pas)
- {
- parent->AddRef();
- }
-
- AVIReadTunnelStream::~AVIReadTunnelStream() {
- pas->Release();
- parent->Release();
- }
-
- HRESULT AVIReadTunnelStream::BeginStreaming(long lStart, long lEnd, long lRate) {
- return AVIStreamBeginStreaming(pas, lStart, lEnd, lRate);
- }
-
- HRESULT AVIReadTunnelStream::EndStreaming() {
- return AVIStreamEndStreaming(pas);
- }
-
- HRESULT AVIReadTunnelStream::Info(AVISTREAMINFO *pasi, long lSize) {
- return AVIStreamInfo(pas, pasi, lSize);
- }
-
- bool AVIReadTunnelStream::IsKeyFrame(long lFrame) {
- return !!AVIStreamIsKeyFrame(pas, lFrame);
- }
-
- HRESULT AVIReadTunnelStream::Read(long lStart, long lSamples, void *lpBuffer, long cbBuffer, long *plBytes, long *plSamples) {
- HRESULT hr;
-
- hr = AVIStreamRead(pas, lStart, lSamples, lpBuffer, cbBuffer, plBytes, plSamples);
-
- if (pAvisynthClipInfo) {
- const char *pszErr;
-
- if (pAvisynthClipInfo->GetError(&pszErr))
- throw MyError("Avisynth read error:\n%s", pszErr);
- }
-
- return hr;
- }
-
- long AVIReadTunnelStream::Start() {
- return AVIStreamStart(pas);
- }
-
- long AVIReadTunnelStream::End() {
- return AVIStreamEnd(pas);
- }
-
- long AVIReadTunnelStream::PrevKeyFrame(long lFrame) {
- return AVIStreamPrevKeyFrame(pas, lFrame);
- }
-
- long AVIReadTunnelStream::NextKeyFrame(long lFrame) {
- return AVIStreamNextKeyFrame(pas, lFrame);
- }
-
- long AVIReadTunnelStream::NearestKeyFrame(long lFrame) {
- return AVIStreamNearestKeyFrame(pas, lFrame);
- }
-
- HRESULT AVIReadTunnelStream::FormatSize(long lFrame, long *plSize) {
- return AVIStreamFormatSize(pas, lFrame, plSize);
- }
-
- HRESULT AVIReadTunnelStream::ReadFormat(long lFrame, void *pFormat, long *plSize) {
- return AVIStreamReadFormat(pas, lFrame, pFormat, plSize);
- }
-
- bool AVIReadTunnelStream::isStreaming() {
- return false;
- }
-
- bool AVIReadTunnelStream::isKeyframeOnly() {
- return false;
- }
-
- ///////////////////////////////////////////////////////////////////////////
-
- class AVIReadStream : public IAVIReadStream, public ListNode2<AVIReadStream> {
- friend AVIReadHandler;
-
- public:
- AVIReadStream(AVIReadHandler *, AVIStreamNode *, int);
- ~AVIReadStream();
-
- HRESULT BeginStreaming(long lStart, long lEnd, long lRate);
- HRESULT EndStreaming();
- HRESULT Info(AVISTREAMINFO *pasi, long lSize);
- bool IsKeyFrame(long lFrame);
- HRESULT Read(long lStart, long lSamples, void *lpBuffer, long cbBuffer, long *plBytes, long *plSamples);
- long Start();
- long End();
- long PrevKeyFrame(long lFrame);
- long NextKeyFrame(long lFrame);
- long NearestKeyFrame(long lFrame);
- HRESULT FormatSize(long lFrame, long *plSize);
- HRESULT ReadFormat(long lFrame, void *pFormat, long *plSize);
- bool isStreaming();
- bool isKeyframeOnly();
- void Reinit();
-
- private:
- AVIReadHandler *parent;
- AVIStreamNode *psnData;
- AVIIndexEntry2 *pIndex;
- AVIReadCache *rCache;
- long& length;
- long& frames;
- long sampsize;
- int streamno;
- bool fStreamingEnabled;
- bool fStreamingActive;
- int iStreamTrackCount;
- long lStreamTrackValue;
- long lStreamTrackInterval;
- bool fRealTime;
-
- __int64 i64CachedPosition;
- AVIIndexEntry2 *pCachedEntry;
-
- };
-
- ///////////////////////////////////////////////////////////////////////////
-
- AVIReadStream::AVIReadStream(AVIReadHandler *parent, AVIStreamNode *psnData, int streamno)
- :length(psnData->length)
- ,frames(psnData->frames)
- {
- this->parent = parent;
- this->psnData = psnData;
- this->streamno = streamno;
-
- fStreamingEnabled = false;
- fStreamingActive = false;
- fRealTime = false;
-
- parent->AddRef();
-
- pIndex = psnData->index.index2Ptr();
- sampsize = psnData->hdr.dwSampleSize;
-
- // Hack to imitate Microsoft's parser. It seems to ignore this value
- // for audio streams.
-
- if (psnData->hdr.fccType == streamtypeAUDIO)
- sampsize = ((WAVEFORMATEX *)psnData->pFormat)->nBlockAlign;
-
- if (sampsize) {
- i64CachedPosition = 0;
- pCachedEntry = pIndex;
- }
-
- psnData->listHandlers.AddTail(this);
- }
-
- AVIReadStream::~AVIReadStream() {
- EndStreaming();
- parent->Release();
- Remove();
- }
-
- void AVIReadStream::Reinit() {
- pIndex = psnData->index.index2Ptr();
- i64CachedPosition = 0;
- pCachedEntry = pIndex;
- }
-
- HRESULT AVIReadStream::BeginStreaming(long lStart, long lEnd, long lRate) {
- if (fStreamingEnabled)
- return 0;
-
- // OutputDebugString(lRate>1500 ? "starting: fast" : "starting: slow");
-
- if (lRate <= 1500) {
- parent->AdjustRealTime(true);
- fRealTime = true;
- } else
- fRealTime = false;
-
- if (parent->fDisableFastIO)
- return 0;
-
- if (!psnData->streaming_count) {
- // if (!(psnData->cache = new AVIReadCache(psnData->hdr.fccType == 'sdiv' ? 65536 : 16384, streamno, parent, psnData)))
- // return AVIERR_MEMORY;
-
- psnData->stream_bytes = 0;
- psnData->stream_pushes = 0;
- psnData->stream_push_pos = 0;
- }
- ++psnData->streaming_count;
-
- fStreamingEnabled = true;
- fStreamingActive = false;
- iStreamTrackCount = 0;
- lStreamTrackValue = -1;
- lStreamTrackInterval = -1;
- return 0;
- }
-
- HRESULT AVIReadStream::EndStreaming() {
- if (!fStreamingEnabled)
- return 0;
-
- if (fRealTime)
- parent->AdjustRealTime(false);
-
- if (fStreamingActive)
- parent->DisableStreaming(streamno);
-
- fStreamingEnabled = false;
- fStreamingActive = false;
-
- if (!--psnData->streaming_count) {
- delete psnData->cache;
- psnData->cache = NULL;
- }
- return 0;
- }
-
- HRESULT AVIReadStream::Info(AVISTREAMINFO *pasi, long lSize) {
- AVISTREAMINFO asi;
-
- memset(&asi, 0, sizeof asi);
-
- asi.fccType = psnData->hdr.fccType;
- asi.fccHandler = psnData->hdr.fccHandler;
- asi.dwFlags = psnData->hdr.dwFlags;
- asi.wPriority = psnData->hdr.wPriority;
- asi.wLanguage = psnData->hdr.wLanguage;
- asi.dwScale = psnData->hdr.dwScale;
- asi.dwRate = psnData->hdr.dwRate;
- asi.dwStart = psnData->hdr.dwStart;
- asi.dwLength = psnData->hdr.dwLength;
- asi.dwInitialFrames = psnData->hdr.dwInitialFrames;
- asi.dwSuggestedBufferSize = psnData->hdr.dwSuggestedBufferSize;
- asi.dwQuality = psnData->hdr.dwQuality;
- asi.dwSampleSize = psnData->hdr.dwSampleSize;
- asi.rcFrame.top = psnData->hdr.rcFrame.top;
- asi.rcFrame.left = psnData->hdr.rcFrame.left;
- asi.rcFrame.right = psnData->hdr.rcFrame.right;
- asi.rcFrame.bottom = psnData->hdr.rcFrame.bottom;
-
- if (lSize < sizeof asi)
- memcpy(pasi, &asi, lSize);
- else {
- memcpy(pasi, &asi, sizeof asi);
- memset((char *)pasi + sizeof asi, 0, lSize - sizeof asi);
- }
-
- return 0;
- }
-
- bool AVIReadStream::IsKeyFrame(long lFrame) {
- if (sampsize)
- return true;
- else {
- if (lFrame < 0 || lFrame >= length)
- return false;
-
- return !(pIndex[lFrame].size & 0x80000000);
- }
- }
-
- HRESULT AVIReadStream::Read(long lStart, long lSamples, void *lpBuffer, long cbBuffer, long *plBytes, long *plSamples) {
- long lActual;
-
- if (lStart < 0 || lStart >= length || (lSamples <= 0 && lSamples != AVISTREAMREAD_CONVENIENT)) {
- // umm... dummy! can't read outside of stream!
-
- if (plBytes) *plBytes = 0;
- if (plSamples) *plSamples = 0;
-
- return 0;
- }
-
- // blocked or discrete?
-
- if (sampsize) {
- AVIIndexEntry2 *avie2, *avie2_limit = pIndex+frames;
- __int64 byte_off = (__int64)lStart * sampsize;
- __int64 bytecnt;
- __int64 actual_bytes=0;
- __int64 block_pos;
-
- // too small to hold a sample?
-
- if (lpBuffer && cbBuffer < sampsize) {
- if (plBytes) *plBytes = sampsize * lSamples;
- if (plSamples) *plSamples = lSamples;
-
- return AVIERR_BUFFERTOOSMALL;
- }
-
- // find the frame that has the starting sample -- try and work
- // from our last position to save time
-
- if (byte_off >= i64CachedPosition) {
- block_pos = i64CachedPosition;
- avie2 = pCachedEntry;
- byte_off -= block_pos;
- } else {
- block_pos = 0;
- avie2 = pIndex;
- }
-
- while(byte_off >= (avie2->size & 0x7FFFFFFF)) {
- byte_off -= (avie2->size & 0x7FFFFFFF);
- block_pos += (avie2->size & 0x7FFFFFFF);
- ++avie2;
- }
-
- pCachedEntry = avie2;
- i64CachedPosition = block_pos;
-
- // Client too lazy to specify a size?
-
- if (lSamples == AVISTREAMREAD_CONVENIENT) {
- lSamples = ((avie2->size & 0x7FFFFFFF) - (long)byte_off) / sampsize;
-
- if (!lSamples && avie2+1 < avie2_limit)
- lSamples = ((avie2[0].size & 0x7FFFFFFF) + (avie2[1].size & 0x7FFFFFFF) - (long)byte_off) / sampsize;
-
- if (lSamples < 0)
- lSamples = 1;
- }
-
- // trim down sample count
-
- if (lpBuffer && lSamples > cbBuffer / sampsize)
- lSamples = cbBuffer / sampsize;
-
- if (lStart+lSamples > length)
- lSamples = length - lStart;
-
- bytecnt = lSamples * sampsize;
-
- // begin reading frames from this point on
-
- if (lpBuffer) {
- // detect streaming
-
- if (fStreamingEnabled) {
-
- // We consider the client to be streaming if we detect at least
- // 3 consecutive accesses
-
- if (lStart == lStreamTrackValue) {
- ++iStreamTrackCount;
-
- if (iStreamTrackCount >= 15) {
-
- __int64 streamptr = parent->getStreamPtr();
- __int64 fptrdiff = streamptr - avie2->pos;
-
- if (!parent->isStreaming() || streamptr<0 || (fptrdiff<4194304 && fptrdiff>-4194304)) {
- if (!psnData->cache)
- psnData->cache = new AVIReadCache(psnData->hdr.fccType == 'sdiv' ? 131072 : 16384, streamno, parent, psnData);
- else
- psnData->cache->ResetStatistics();
-
- if (!fStreamingActive) {
- fStreamingActive = true;
- parent->EnableStreaming(streamno);
- }
-
- #ifdef STREAMING_DEBUG
- OutputDebugString("[a] streaming enabled\n");
- #endif
- }
- } else {
- #ifdef STREAMING_DEBUG
- OutputDebugString("[a] streaming detected\n");
- #endif
- }
- } else {
- #ifdef STREAMING_DEBUG
- OutputDebugString("[a] streaming disabled\n");
- #endif
- iStreamTrackCount = 0;
-
- if (fStreamingActive) {
- fStreamingActive = false;
- parent->DisableStreaming(streamno);
- }
- }
- }
-
- while(bytecnt > 0) {
- long tc;
-
- tc = (avie2->size & 0x7FFFFFFF) - (long)byte_off;
- if (tc > bytecnt)
- tc = (long)bytecnt;
-
- if (psnData->cache) {
- //OutputDebugString("[a] attempting cached read\n");
- lActual = psnData->cache->Read(lpBuffer, avie2->pos, avie2->pos + byte_off + 8, tc);
- psnData->stream_bytes += lActual;
- } else
- lActual = parent->ReadData(streamno, lpBuffer, avie2->pos + byte_off + 8, tc);
-
- if (lActual < 0)
- break;
-
- actual_bytes += lActual;
- ++avie2;
- byte_off = 0;
-
- if (lActual < tc)
- break;
-
- bytecnt -= tc;
- lpBuffer = (char *)lpBuffer + tc;
- }
-
- if (actual_bytes < sampsize) {
- if (plBytes) *plBytes = 0;
- if (plSamples) *plSamples = 0;
- return AVIERR_FILEREAD;
- }
-
- actual_bytes -= actual_bytes % sampsize;
-
- if (plBytes) *plBytes = (long)actual_bytes;
- if (plSamples) *plSamples = (long)actual_bytes / sampsize;
-
- lStreamTrackValue = lStart + (long)actual_bytes / sampsize;
-
- } else {
- if (plBytes) *plBytes = (long)bytecnt;
- if (plSamples) *plSamples = lSamples;
- }
-
- } else {
- AVIIndexEntry2 *avie2 = &pIndex[lStart];
-
- if (lpBuffer && (avie2->size & 0x7FFFFFFF) > cbBuffer) {
- if (plBytes) *plBytes = avie2->size & 0x7FFFFFFF;
- if (plSamples) *plSamples = 1;
-
- return AVIERR_BUFFERTOOSMALL;
- }
-
- if (lpBuffer) {
-
- // detect streaming
-
- if (fStreamingEnabled && lStart != lStreamTrackValue) {
- if (lStreamTrackValue>=0 && lStart-lStreamTrackValue == lStreamTrackInterval) {
- if (++iStreamTrackCount >= 15) {
-
- __int64 streamptr = parent->getStreamPtr();
- __int64 fptrdiff = streamptr - avie2->pos;
-
- if (!parent->isStreaming() || streamptr<0 || (fptrdiff<4194304 && fptrdiff>-4194304)) {
- if (!psnData->cache)
- psnData->cache = new AVIReadCache(psnData->hdr.fccType == 'sdiv' ? 131072 : 16384, streamno, parent, psnData);
- else
- psnData->cache->ResetStatistics();
-
- if (!fStreamingActive) {
- fStreamingActive = true;
- parent->EnableStreaming(streamno);
- }
-
- #ifdef STREAMING_DEBUG
- OutputDebugString("[v] streaming activated\n");
- #endif
- }
- } else {
- #ifdef STREAMING_DEBUG
- OutputDebugString("[v] streaming detected\n");
- #endif
- }
- } else {
- iStreamTrackCount = 0;
- #ifdef STREAMING_DEBUG
- OutputDebugString("[v] streaming disabled\n");
- #endif
- if (lStreamTrackValue>=0 && lStart > lStreamTrackValue) {
- lStreamTrackInterval = lStart - lStreamTrackValue;
- } else
- lStreamTrackInterval = -1;
-
- if (fStreamingActive) {
- fStreamingActive = false;
- parent->DisableStreaming(streamno);
- }
- }
-
- lStreamTrackValue = lStart;
- }
-
- // read data
-
- if (psnData->cache && fStreamingActive) {
- //OutputDebugString("[v] attempting cached read\n");
- lActual = psnData->cache->Read(lpBuffer, avie2->pos, avie2->pos + 8, avie2->size & 0x7FFFFFFF);
- psnData->stream_bytes += lActual;
- } else
- lActual = parent->ReadData(streamno, lpBuffer, avie2->pos+8, avie2->size & 0x7FFFFFFF);
-
- if (lActual != (avie2->size & 0x7FFFFFFF)) {
- if (plBytes) *plBytes = 0;
- if (plSamples) *plSamples = 0;
- return AVIERR_FILEREAD;
- }
- }
-
- if (plBytes) *plBytes = avie2->size & 0x7FFFFFFF;
- if (plSamples) *plSamples = 1;
- }
-
- if (psnData->cache && fStreamingActive) {
-
- // Are we experiencing a high rate of cache misses?
-
- if (psnData->cache->cache_miss_bytes*2 > psnData->cache->cache_hit_bytes && psnData->cache->reads > 50) {
-
- // sh*t, notify the parent that we have cache misses so it can check which stream is
- // screwing up, and disable streaming on feeds that are too far off
-
- parent->FixCacheProblems(this);
- iStreamTrackCount = 0;
- }
- }/* else if (fStreamingEnabled) {
-
- // hmm... our cache got killed!
-
- iStreamTrackCount = 0;
-
- }*/
-
- return 0;
- }
-
- long AVIReadStream::Start() {
- return 0;
- }
-
- long AVIReadStream::End() {
- return length;
- }
-
- long AVIReadStream::PrevKeyFrame(long lFrame) {
- if (sampsize)
- return lFrame>0 ? lFrame-1 : -1;
-
- if (lFrame < 0)
- return -1;
-
- if (lFrame >= length)
- lFrame = length;
-
- while(--lFrame > 0)
- if (!(pIndex[lFrame].size & 0x80000000))
- return lFrame;
-
- return -1;
- }
-
- long AVIReadStream::NextKeyFrame(long lFrame) {
- if (sampsize)
- return lFrame<length ? lFrame+1 : -1;
-
- if (lFrame < 0)
- return 0;
-
- if (lFrame >= length)
- return -1;
-
- while(++lFrame < length)
- if (!(pIndex[lFrame].size & 0x80000000))
- return lFrame;
-
- return -1;
- }
-
- long AVIReadStream::NearestKeyFrame(long lFrame) {
- long lprev;
-
- if (sampsize)
- return lFrame;
-
- if (IsKeyFrame(lFrame))
- return lFrame;
-
- lprev = PrevKeyFrame(lFrame);
-
- if (lprev < 0)
- return 0;
- else
- return lprev;
- }
-
- HRESULT AVIReadStream::FormatSize(long lFrame, long *plSize) {
- *plSize = psnData->lFormatLen;
- return 0;
- }
-
- HRESULT AVIReadStream::ReadFormat(long lFrame, void *pFormat, long *plSize) {
- if (!pFormat) {
- *plSize = psnData->lFormatLen;
- return 0;
- }
-
- if (*plSize < psnData->lFormatLen) {
- memcpy(pFormat, psnData->pFormat, *plSize);
- } else {
- memcpy(pFormat, psnData->pFormat, psnData->lFormatLen);
- *plSize = psnData->lFormatLen;
- }
-
- return 0;
- }
-
- bool AVIReadStream::isStreaming() {
- return psnData->cache && fStreamingActive;
- }
-
- bool AVIReadStream::isKeyframeOnly() {
- return psnData->keyframe_only;
- }
-
- ///////////////////////////////////////////////////////////////////////////
-
- AVIReadHandler::AVIReadHandler(const char *s)
- : pAvisynthClipInfo(0)
- {
- this->hFile = INVALID_HANDLE_VALUE;
- this->hFileUnbuffered = INVALID_HANDLE_VALUE;
- this->paf = NULL;
- ref_count = 1;
- streams=0;
- fStreamsActive = 0;
- fDisableFastIO = false;
- streamBuffer = NULL;
- nRealTime = 0;
- nActiveStreamers = 0;
- fFakeIndex = false;
- nFiles = 1;
- nCurrentFile = 0;
- pSegmentHint = NULL;
-
- if (!g_disklockinited) {
- g_disklockinited=true;
- InitializeCriticalSection(&g_diskcs);
- }
-
- _construct(s);
- }
-
- AVIReadHandler::AVIReadHandler(PAVIFILE paf) {
- this->hFile = INVALID_HANDLE_VALUE;
- this->hFileUnbuffered = INVALID_HANDLE_VALUE;
- this->paf = paf;
- ref_count = 1;
- streams=0;
- streamBuffer = NULL;
- pSegmentHint = NULL;
- fFakeIndex = false;
-
- if (FAILED(paf->QueryInterface(IID_IAvisynthClipInfo, (void **)&pAvisynthClipInfo)))
- pAvisynthClipInfo = NULL;
- else {
- const char *s;
-
- if (pAvisynthClipInfo->GetError(&s)) {
- MyError e("Avisynth open failure:\n%s", s);
- pAvisynthClipInfo->Release();
- paf->Release();
- throw e;
- }
- }
- }
-
- AVIReadHandler::~AVIReadHandler() {
- _destruct();
- }
-
- void AVIReadHandler::_construct(const char *pszFile) {
-
- try {
- AVIFileDesc *pDesc;
-
- // open file
-
- hFile = CreateFile(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
-
- if (INVALID_HANDLE_VALUE == hFile)
- throw MyWin32Error("Couldn't open %s: %%s", GetLastError(), pszFile);
-
- hFileUnbuffered = CreateFile(
- pszFile,
- GENERIC_READ,
- FILE_SHARE_READ,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING,
- NULL
- );
-
- i64FilePosition = 0;
-
- // recursively parse file
-
- _parseFile(listStreams);
-
- // Create first link
-
- if (!(pDesc = new AVIFileDesc))
- throw MyMemoryError();
-
- pDesc->hFile = hFile;
- pDesc->hFileUnbuffered = hFileUnbuffered;
- pDesc->i64Size = i64Size = _sizeFile();
-
- listFiles.AddHead(pDesc);
-
- } catch(...) {
- _destruct();
- throw;
- }
- }
-
- bool AVIReadHandler::AppendFile(const char *pszFile) {
- List2<AVIStreamNode> newstreams;
- AVIStreamNode *pasn_old, *pasn_new, *pasn_old_next=NULL, *pasn_new_next=NULL;
- AVIFileDesc *pDesc;
-
- nCurrentFile = -1;
-
- // open file
-
- hFile = CreateFile(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
-
- if (INVALID_HANDLE_VALUE == hFile)
- throw MyWin32Error("Couldn't open %s: %%s", GetLastError(), pszFile);
-
- hFileUnbuffered = CreateFile(
- pszFile,
- GENERIC_READ,
- FILE_SHARE_READ,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING,
- NULL
- );
-
- try {
- _parseFile(newstreams);
-
- pasn_old = listStreams.AtHead();
- pasn_new = newstreams.AtHead();
-
- while(!!(pasn_old_next = pasn_old->NextFromHead()) & !!(pasn_new_next = pasn_new->NextFromHead())) {
- const char *szPrefix;
-
- switch(pasn_old->hdr.fccType) {
- case streamtypeAUDIO: szPrefix = "Cannot append segment: The audio streams "; break;
- case streamtypeVIDEO: szPrefix = "Cannot append segment: The video streams "; break;
- default: szPrefix = NULL;
- }
-
- // If it's not an audio or video stream, why do we care?
-
- if (szPrefix) {
- if (pasn_old->hdr.fccType != pasn_new->hdr.fccType)
- throw MyError("Cannot append segment: The stream types do not count.");
-
- if (pasn_old->hdr.fccHandler != pasn_new->hdr.fccHandler)
- throw MyError("%suse incompatible compression types.", szPrefix);
-
- // A/B ?= C/D ==> AD ?= BC
-
- if ((__int64)pasn_old->hdr.dwScale * pasn_new->hdr.dwRate != (__int64)pasn_new->hdr.dwScale * pasn_old->hdr.dwRate)
- throw MyError("%shave different sampling rates (%.5f vs. %.5f)"
- ,szPrefix
- ,(double)pasn_old->hdr.dwRate / pasn_old->hdr.dwScale
- ,(double)pasn_new->hdr.dwRate / pasn_new->hdr.dwScale
- );
-
- if (pasn_old->hdr.dwSampleSize != pasn_new->hdr.dwSampleSize)
- throw MyError("%shave different block sizes (%d vs %d).", szPrefix, pasn_old->hdr.dwSampleSize, pasn_new->hdr.dwSampleSize);
-
- // I hate PCMWAVEFORMAT.
-
- bool fOk = pasn_old->lFormatLen == pasn_new->lFormatLen && !memcmp(pasn_old->pFormat, pasn_new->pFormat, pasn_old->lFormatLen);
-
- if (pasn_old->hdr.fccType == streamtypeAUDIO)
- if (((WAVEFORMATEX *)pasn_old->pFormat)->wFormatTag == WAVE_FORMAT_PCM
- && ((WAVEFORMATEX *)pasn_new->pFormat)->wFormatTag == WAVE_FORMAT_PCM)
- fOk = !memcmp(pasn_old->pFormat, pasn_new->pFormat, sizeof(PCMWAVEFORMAT));
-
- if (!fOk)
- throw MyError("%shave different data formats.", szPrefix);
- }
-
- pasn_old = pasn_old_next;
- pasn_new = pasn_new_next;
- }
-
- if (pasn_old_next || pasn_new_next)
- throw MyError("Cannot append segment: The segment has a different number of streams.");
-
- if (!(pDesc = new AVIFileDesc))
- throw MyMemoryError();
-
- pDesc->hFile = hFile;
- pDesc->hFileUnbuffered = hFileUnbuffered;
- pDesc->i64Size = _sizeFile();
- } catch(MyError e) {
- while(pasn_new = newstreams.RemoveHead())
- delete pasn_new;
-
- CloseHandle(hFile);
- if (hFileUnbuffered != INVALID_HANDLE_VALUE)
- CloseHandle(hFileUnbuffered);
-
- throw;
- }
-
- // Accept segment; begin merging process.
-
- pasn_old = listStreams.AtHead();
-
- while(pasn_old_next = pasn_old->NextFromHead()) {
- pasn_new = newstreams.RemoveHead();
-
- // Fix up header.
-
- pasn_old->hdr.dwLength += pasn_new->hdr.dwLength;
-
- if (pasn_new->hdr.dwSuggestedBufferSize > pasn_old->hdr.dwSuggestedBufferSize)
- pasn_old->hdr.dwSuggestedBufferSize = pasn_new->hdr.dwSuggestedBufferSize;
-
- pasn_old->bytes += pasn_new->bytes;
- pasn_old->frames += pasn_new->frames;
- pasn_old->length += pasn_new->length;
-
- // Merge indices.
-
- int oldlen = pasn_old->index.indexLen();
- AVIIndexEntry2 *idx_old = pasn_old->index.takeIndex2();
- AVIIndexEntry2 *idx_new = pasn_new->index.index2Ptr();
- int i;
-
- pasn_old->index.clear();
-
- for(i=0; i<oldlen; i++) {
- idx_old[i].size ^= 0x80000000;
- pasn_old->index.add(&idx_old[i]);
- }
-
- delete[] idx_old;
-
- for(i=pasn_new->index.indexLen(); i; i--) {
- idx_new->size ^= 0x80000000;
- idx_new->pos += (__int64)nFiles << 48;
- pasn_old->index.add(idx_new++);
- }
-
- pasn_old->index.makeIndex2();
-
- // Notify all handlers.
-
- AVIReadStream *pStream, *pStreamNext;
-
- pStream = pasn_old->listHandlers.AtHead();
- while(pStreamNext = pStream->NextFromHead()) {
- pStream->Reinit();
-
- pStream = pStreamNext;
- }
-
- // Next!
-
- pasn_old = pasn_old_next;
- delete pasn_new;
- }
-
- ++nFiles;
- listFiles.AddTail(pDesc);
-
- return true;
- }
-
- void AVIReadHandler::_parseFile(List2<AVIStreamNode>& streamlist) {
- FOURCC fccType;
- DWORD dwLength;
- DWORD dwLengthLeft;
- bool index_found = false;
- bool size_invalid = false;
- bool fAcceptIndexOnly = true;
- bool hyperindexed = false;
- AVIStreamNode *pasn, *pasn_next;
-
- __int64 i64ChunkMoviPos = 0;
- DWORD dwChunkMoviLength = 0;
-
- if (!_readChunkHeader(fccType, dwLength))
- throw MyError("Invalid AVI file: File is less than 8 bytes");
-
- if (fccType != FOURCC_RIFF)
- throw MyError("Invalid AVI file: Not a RIFF file");
-
- // If the RIFF header is <4 bytes, assume it was an improperly closed
- // file.
-
- // if (dwLength < 4)
- // throw MyError("Invalid AVI file: RIFF wrapper has invalid size");
-
- if (dwLength < 4) {
- dwLength = 0xFFFFFFF0;
- size_invalid = true;
- }
-
- dwLengthLeft = dwLength-4;
-
- _readFile2(&fccType, 4);
-
- if (fccType != ' IVA')
- throw MyError("Invalid AVI file: RIFF type is not 'AVI'");
-
- // begin parsing chunks
-
- while(dwLengthLeft >= 8 && _readChunkHeader(fccType, dwLength)) {
-
- // _RPT4(0,"%08I64x %08I64x Chunk '%-4s', length %08lx\n", _posFile()+dwLengthLeft, _posFile(), &fccType, dwLength);
-
- if (!size_invalid) {
- dwLengthLeft -= 8;
-
- if (dwLength > dwLengthLeft)
- throw MyError("Invalid AVI file: chunk at %I64d extends outside of parent by %ld bytes", _posFile()-8, dwLength-dwLengthLeft);
-
- dwLengthLeft -= (dwLength + (dwLength&1));
- }
-
- switch(fccType) {
- case FOURCC_LIST:
- _readFile2(&fccType, 4);
-
- if (dwLength<4 && (fccType != 'ivom' || !size_invalid))
- throw MyError("Invalid AVI file: LIST chunk <4 bytes");
-
- dwLength -= 4;
-
- // _RPT1(0,"\tList type '%-4s'\n", &fccType);
-
- switch(fccType) {
- case 'ivom':
-
- if (dwLength < 8 && size_invalid) {
- i64ChunkMoviPos = _posFile();
- dwChunkMoviLength = 0xFFFFFFF0;
- dwLength = 0;
- } else {
- i64ChunkMoviPos = _posFile();
- dwChunkMoviLength = dwLength;
- }
-
- if (fAcceptIndexOnly)
- goto terminate_scan;
-
- break;
- case listtypeAVIHEADER:
- dwLengthLeft += (dwLength + (dwLength&1)) + 4;
- dwLength = 0; // silently enter the header block
- break;
- case listtypeSTREAMHEADER:
- if (!_parseStreamHeader(streamlist, dwLength))
- fAcceptIndexOnly = false;
- else
- hyperindexed = true;
-
- ++streams;
- dwLength = 0;
- break;
- }
-
- break;
-
- case ckidAVINEWINDEX: // idx1
- if (!hyperindexed) {
- index_found = _parseIndexBlock(streamlist, dwLength/16, i64ChunkMoviPos);
- dwLength &= 15;
- }
- break;
-
- case ckidAVIPADDING: // JUNK
- break;
-
- case 'mges': // VirtualDub segment hint block
- delete pSegmentHint;
- if (!(pSegmentHint = new char[dwLength]))
- throw MyMemoryError();
-
- _readFile2(pSegmentHint, dwLength);
-
- if (dwLength&1)
- _skipFile2(1);
-
- dwLength = 0;
- break;
-
- }
-
- if (dwLength) {
- if (!_skipFile2(dwLength + (dwLength&1)))
- break;
- }
-
- // If we're trying to read a broken file, quit as soon as we see the index
- // block.
-
- if (size_invalid && fccType == ckidAVINEWINDEX)
- break;
- }
-
- if (i64ChunkMoviPos == 0)
- throw MyError("This AVI file doesn't have a movie data block (movi)!");
-
- terminate_scan:
- if (!index_found && !hyperindexed) {
- ProgressDialog pd(NULL, "AVI Import Filter", "Reconstructing missing index block", (dwChunkMoviLength+1023)/1024, true);
-
- pd.setValueFormat("%ldK of %ldK");
-
- // obtain length of file and limit scanning if so
-
- __int64 i64FileSize = 0x7FFFFFFFFFFFFFFFi64;
-
- dwLengthLeft = dwChunkMoviLength;
-
- {
- DWORD dwLow, dwHigh;
-
- dwLow = GetFileSize(hFile, &dwHigh);
-
- if (dwLow != 0xFFFFFFFF || GetLastError()==NO_ERROR) {
- i64FileSize = (__int64)(unsigned long)dwLow + ((__int64)(signed long)dwHigh << 32);
- }
- }
-
- fFakeIndex = true;
-
- _seekFile(i64ChunkMoviPos);
-
- while(dwLengthLeft >= 8 && _readChunkHeader(fccType, dwLength)) {
- int stream;
-
- // _RPT2(0,"(stream header) Chunk '%-4s', length %08lx\n", &fccType, dwLength);
-
- dwLengthLeft -= 8+(dwLength + (dwLength&1));
-
- if (dwLength) {
- if (!_skipFile2(dwLength + (dwLength&1)))
- break;
- }
-
- if (_posFile() > i64FileSize)
- break;
-
- if (isxdigit(fccType&0xff) && isxdigit((fccType>>8)&0xff)) {
- stream = StreamFromFOURCC(fccType);
-
- if (stream >=0 && stream < streams) {
-
- pasn = streamlist.AtHead();
-
- while(stream-- && (pasn_next = pasn->NextFromHead()))
- pasn = pasn_next;
-
- if (pasn && pasn_next) {
- pasn->index.add(fccType, _posFile()-(dwLength + (dwLength&1))-8, dwLength, pasn->keyframe_only && pasn->bytes!=0);
- pasn->bytes += dwLength;
- }
- }
-
- }
-
- pd.advance((long)((_posFile() - i64ChunkMoviPos)/1024));
- pd.check();
- }
-
- }
-
- // glue together indices
-
- pasn = streamlist.AtHead();
-
- while(pasn_next = pasn->NextFromHead()) {
- if (!pasn->index.makeIndex2())
- throw MyMemoryError();
-
- pasn->frames = pasn->index.indexLen();
-
- // Clear sample size for video streams!
-
- if (pasn->hdr.fccType == streamtypeVIDEO)
- pasn->hdr.dwSampleSize=0;
-
- if (pasn->hdr.dwSampleSize)
- pasn->length = (long)pasn->bytes / pasn->hdr.dwSampleSize;
- else
- pasn->length = pasn->frames;
-
- pasn = pasn_next;
- }
-
- // throw MyError("Parse complete. Aborting.");
- }
-
- bool AVIReadHandler::_parseStreamHeader(List2<AVIStreamNode>& streamlist, DWORD dwLengthLeft) {
- AVIStreamNode *pasn;
- FOURCC fccType;
- DWORD dwLength;
- bool hyperindexed = false;
-
- if (!(pasn = new AVIStreamNode()))
- throw MyMemoryError();
-
- try {
- while(dwLengthLeft >= 8 && _readChunkHeader(fccType, dwLength)) {
-
- // _RPT2(0,"(stream header) Chunk '%-4s', length %08lx\n", &fccType, dwLength);
-
- dwLengthLeft -= 8;
-
- if (dwLength > dwLengthLeft)
- throw MyError("Invalid AVI file: chunk size extends outside of parent");
-
- dwLengthLeft -= (dwLength + (dwLength&1));
-
- switch(fccType) {
-
- case ckidSTREAMHEADER:
- memset(&pasn->hdr, 0, sizeof pasn->hdr);
-
- if (dwLength < sizeof pasn->hdr) {
- _readFile2(&pasn->hdr, dwLength);
- if (dwLength & 1)
- _skipFile(1);
- } else {
- _readFile2(&pasn->hdr, sizeof pasn->hdr);
- _skipFile(dwLength+(dwLength&1) - sizeof pasn->hdr);
- }
- dwLength = 0;
-
- pasn->keyframe_only = false;
-
- break;
-
- case ckidSTREAMFORMAT:
- if (!(pasn->pFormat = new char[pasn->lFormatLen = dwLength]))
- throw MyMemoryError();
-
- _readFile2(pasn->pFormat, dwLength);
-
- if (pasn->hdr.fccType == streamtypeVIDEO) {
- switch(((BITMAPINFOHEADER *)pasn->pFormat)->biCompression) {
- case NULL:
- case ' WAR':
- case ' BID':
- case '1bmd':
- case 'gpjm':
- case 'GPJM':
- case 'YUYV':
- case '2YUY':
- case 'YVYU':
- case 'UYVY':
- case '21VY':
- case '024I':
- case 'P14Y':
- case 'vuyc':
- case 'UYFH':
- pasn->keyframe_only = true;
- }
- }
-
- if (dwLength & 1)
- _skipFile(1);
- dwLength = 0;
- break;
-
- case 'xdni': // OpenDML extended index
- _parseExtendedIndexBlock(streamlist, pasn, -1, dwLength);
- hyperindexed = true;
- break;
-
- case ckidAVIPADDING: // JUNK
- break;
- }
-
- if (dwLength) {
- if (!_skipFile2(dwLength + (dwLength&1)))
- break;
- }
- }
-
- if (dwLengthLeft)
- _skipFile2(dwLengthLeft);
- } catch(...) {
- delete pasn;
- throw;
- }
-
- // _RPT1(0,"Found stream: type %s\n", pasn->hdr.fccType==streamtypeVIDEO ? "video" : pasn->hdr.fccType==streamtypeAUDIO ? "audio" : "unknown");
-
- streamlist.AddTail(pasn);
-
- return hyperindexed;
- }
-
- bool AVIReadHandler::_parseIndexBlock(List2<AVIStreamNode>& streamlist, int count, __int64 movi_offset) {
- AVIINDEXENTRY avie[32];
- AVIStreamNode *pasn, *pasn_next;
- bool absolute_addr = true;
-
- // Some AVI files have relative addresses in their AVI index chunks, and some
- // relative. They're supposed to be relative to the 'movi' chunk; all versions
- // of VirtualDub using fast write routines prior to build 4936 generate absolute
- // addresses (oops). AVIFile and ActiveMovie are both ambivalent. I guess we'd
- // better be as well.
-
- while(count > 0) {
- int tc = count;
- int i;
-
- if (tc>32) tc=32;
- count -= tc;
-
- if (tc*sizeof(AVIINDEXENTRY) != _readFile(avie, tc*sizeof(AVIINDEXENTRY))) {
- pasn = streamlist.AtHead();
-
- while(pasn_next = pasn->NextFromHead()) {
- pasn->index.clear();
- pasn->bytes = 0;
-
- pasn = pasn_next;
- }
- return false;
- }
-
- for(i=0; i<tc; i++) {
- int stream = StreamFromFOURCC(avie[i].ckid);
-
- if (absolute_addr && avie[i].dwChunkOffset<movi_offset)
- absolute_addr = false;
-
- pasn = streamlist.AtHead();
-
- while((pasn_next = (AVIStreamNode *)pasn->NextFromHead()) && stream--)
- pasn = pasn_next;
-
- if (pasn && pasn_next) {
- if (absolute_addr)
- pasn->index.add(&avie[i]);
- else
- pasn->index.add(avie[i].ckid, (movi_offset-4) + (__int64)avie[i].dwChunkOffset, avie[i].dwChunkLength, !!(avie[i].dwFlags & AVIIF_KEYFRAME));
-
- pasn->bytes += avie[i].dwChunkLength;
- }
- }
- }
-
- return true;
-
- }
-
- void AVIReadHandler::_parseExtendedIndexBlock(List2<AVIStreamNode>& streamlist, AVIStreamNode *pasn, __int64 fpos, DWORD dwLength) {
- union {
- AVISUPERINDEX idxsuper;
- AVISTDINDEX idxstd;
- };
-
- union {
- struct _avisuperindex_entry superent[64];
- DWORD dwHeap[256];
- };
-
- int entries, tp;
- int i;
- __int64 i64FPSave = _posFile();
-
- if (fpos>=0)
- _seekFile(fpos);
- _readFile2((char *)&idxsuper + 8, sizeof(AVISUPERINDEX) - 8);
-
- switch(idxsuper.bIndexType) {
- case AVI_INDEX_OF_INDEXES:
- // sanity check
-
- if (idxsuper.wLongsPerEntry != 4)
- throw MyError("Invalid superindex block in stream");
-
- // if (idxsuper.bIndexSubType != 0)
- // throw MyError("Field indexes not supported");
-
- entries = idxsuper.nEntriesInUse;
-
- while(entries > 0) {
- tp = sizeof superent / sizeof superent[0];
- if (tp>entries) tp=entries;
-
- _readFile2(superent, tp*sizeof superent[0]);
-
- for(i=0; i<tp; i++)
- _parseExtendedIndexBlock(streamlist, pasn, superent[i].qwOffset+8, superent[i].dwSize-8);
-
- entries -= tp;
- }
-
- break;
-
- case AVI_INDEX_OF_CHUNKS:
-
- // if (idxstd.bIndexSubType != 0)
- // throw MyError("Frame indexes not supported");
-
- entries = idxstd.nEntriesInUse;
-
- // In theory, if bIndexSubType==AVI_INDEX_2FIELD it's supposed to have
- // wLongsPerEntry=3, and bIndexSubType==0 gives wLongsPerEntry=2.
- // Matrox's MPEG-2 stuff generates bIndexSubType=16 and wLongsPerEntry=6.
- // *sigh*
- //
- // For wLongsPerEntry==2 and ==3, dwOffset is at 0 and dwLength at 1;
- // for wLongsPerEntry==6, dwOffset is at 2 and all are keyframes.
-
- {
- if (idxstd.wLongsPerEntry!=2 && idxstd.wLongsPerEntry!=3 && idxstd.wLongsPerEntry!=6)
- throw MyError("Invalid OpenDML index block in stream (wLongsPerEntry=%d)", idxstd.wLongsPerEntry);
-
- while(entries > 0) {
- tp = (sizeof dwHeap / sizeof dwHeap[0]) / idxstd.wLongsPerEntry;
- if (tp>entries) tp=entries;
-
- _readFile2(dwHeap, tp*idxstd.wLongsPerEntry*sizeof(DWORD));
-
- if (idxstd.wLongsPerEntry == 6)
- for(i=0; i<tp; i++) {
- DWORD dwOffset = dwHeap[i*idxstd.wLongsPerEntry + 0];
- DWORD dwSize = dwHeap[i*idxstd.wLongsPerEntry + 2];
-
- pasn->index.add(idxstd.dwChunkId, (idxstd.qwBaseOffset+dwOffset)-8, dwSize, true);
-
- pasn->bytes += dwSize;
- }
- else
- for(i=0; i<tp; i++) {
- DWORD dwOffset = dwHeap[i*idxstd.wLongsPerEntry + 0];
- DWORD dwSize = dwHeap[i*idxstd.wLongsPerEntry + 1];
-
- pasn->index.add(idxstd.dwChunkId, (idxstd.qwBaseOffset+dwOffset)-8, dwSize&0x7FFFFFFF, !(dwSize&0x80000000));
-
- pasn->bytes += dwSize & 0x7FFFFFFF;
- }
-
- entries -= tp;
- }
- }
-
- break;
-
- default:
- throw MyError("Unknown hyperindex type");
- }
-
- _seekFile(i64FPSave);
- }
-
- void AVIReadHandler::_destruct() {
- AVIStreamNode *pasn;
- AVIFileDesc *pDesc;
-
- if (pAvisynthClipInfo)
- pAvisynthClipInfo->Release();
-
- if (paf)
- AVIFileRelease(paf);
-
- while(pasn = listStreams.RemoveTail())
- delete pasn;
-
- delete streamBuffer;
-
- if (listFiles.IsEmpty()) {
- if (hFile != INVALID_HANDLE_VALUE)
- CloseHandle(hFile);
- if (hFileUnbuffered != INVALID_HANDLE_VALUE)
- CloseHandle(hFileUnbuffered);
- } else
- while(pDesc = listFiles.RemoveTail()) {
- CloseHandle(pDesc->hFile);
- CloseHandle(pDesc->hFileUnbuffered);
- delete pDesc;
- }
-
- delete pSegmentHint;
- }
-
- ///////////////////////////////////////////////////////////////////////////
-
- void AVIReadHandler::Release() {
- if (!--ref_count)
- delete this;
- }
-
- void AVIReadHandler::AddRef() {
- ++ref_count;
- }
-
- IAVIReadStream *AVIReadHandler::GetStream(DWORD fccType, LONG lParam) {
- if (paf) {
- PAVISTREAM pas;
- HRESULT hr;
-
- hr = AVIFileGetStream(paf, &pas, fccType, lParam);
-
- if (hr)
- return NULL;
-
- return new AVIReadTunnelStream(this, pas, pAvisynthClipInfo);
- } else {
- AVIStreamNode *pasn, *pasn_next;
- int streamno = 0;
-
- pasn = listStreams.AtHead();
-
- while(pasn_next = pasn->NextFromHead()) {
- if (pasn->hdr.fccType == fccType && !lParam--)
- break;
-
- pasn = pasn_next;
- ++streamno;
- }
-
- if (pasn_next) {
- return new AVIReadStream(this, pasn, streamno);
- }
-
- return NULL;
- }
- }
-
- void AVIReadHandler::EnableFastIO(bool f) {
- fDisableFastIO = !f;
- }
-
- bool AVIReadHandler::isOptimizedForRealtime() {
- return nRealTime!=0;
- }
-
- bool AVIReadHandler::isStreaming() {
- return nActiveStreamers!=0;
- }
-
- bool AVIReadHandler::isIndexFabricated() {
- return fFakeIndex;
- }
-
- bool AVIReadHandler::getSegmentHint(const char **ppszPath) {
- if (!pSegmentHint) {
- if (ppszPath)
- *ppszPath = NULL;
-
- return false;
- }
-
- if (ppszPath)
- *ppszPath = pSegmentHint+1;
-
- return !!pSegmentHint[0];
- }
-
- ///////////////////////////////////////////////////////////////////////////
-
- void AVIReadHandler::EnableStreaming(int stream) {
- if (!fStreamsActive) {
- if (!(streamBuffer = new char[STREAM_SIZE]))
- throw MyMemoryError();
-
- i64StreamPosition = -1;
- sbPosition = sbSize = 0;
- }
-
- fStreamsActive |= (1<<stream);
- ++nActiveStreamers;
- }
-
- void AVIReadHandler::DisableStreaming(int stream) {
- fStreamsActive &= ~(1<<stream);
-
- if (!fStreamsActive) {
- delete streamBuffer;
- streamBuffer = NULL;
- }
- --nActiveStreamers;
- }
-
- void AVIReadHandler::AdjustRealTime(bool fInc) {
- if (fInc)
- ++nRealTime;
- else
- --nRealTime;
- }
-
- char *AVIReadHandler::_StreamRead(long& bytes) {
- if (nCurrentFile<0 || nCurrentFile != (int)(i64StreamPosition>>48))
- _SelectFile((int)(i64StreamPosition>>48));
-
- if (sbPosition >= sbSize) {
- if (nRealTime || (((i64StreamPosition&0x0000FFFFFFFFFFFFi64)+sbSize) & -STREAM_BLOCK_SIZE)+STREAM_SIZE > i64Size) {
- i64StreamPosition += sbSize;
- sbPosition = 0;
- _seekFile(i64StreamPosition & 0x0000FFFFFFFFFFFFi64);
-
- sbSize = _readFile(streamBuffer, STREAM_RT_SIZE);
-
- if (sbSize < 0) {
- sbSize = 0;
- throw MyWin32Error("Failure streaming AVI file: %%s.",GetLastError());
- }
- } else {
- i64StreamPosition += sbSize;
- sbPosition = i64StreamPosition & (STREAM_BLOCK_SIZE-1);
- i64StreamPosition &= -STREAM_BLOCK_SIZE;
- _seekFileUnbuffered(i64StreamPosition & 0x0000FFFFFFFFFFFFi64);
-
- sbSize = _readFileUnbuffered(streamBuffer, STREAM_SIZE);
-
- if (sbSize < 0) {
- sbSize = 0;
- throw MyWin32Error("Failure streaming AVI file: %%s.",GetLastError());
- }
- }
- }
-
- if (sbPosition >= sbSize)
- return NULL;
-
- if (bytes > sbSize - sbPosition)
- bytes = sbSize - sbPosition;
-
- sbPosition += bytes;
-
- return streamBuffer + sbPosition - bytes;
- }
-
- bool AVIReadHandler::Stream(AVIStreamNode *pusher, __int64 pos) {
- bool read_something = false;
-
- if (!streamBuffer)
- return false;
-
- if (i64StreamPosition == -1) {
- i64StreamPosition = pos;
- sbPosition = 0;
- }
-
- if (pos < i64StreamPosition+sbPosition)
- return false;
-
- // >4Mb past current position!?
-
- if (pos > i64StreamPosition+sbPosition+4194304) {
- // OutputDebugString("Resetting streaming position!\n");
- i64StreamPosition = pos;
- sbSize = sbPosition = 0;
- }
-
- /* if (pusher->hdr.fccType == 'sdiv')
- OutputDebugString("pushed by video\n");
- else
- OutputDebugString("pushed by audio\n");*/
-
- ++pusher->stream_pushes;
- pusher->stream_push_pos = pos;
-
- while(pos >= i64StreamPosition+sbPosition) {
- long actual, left;
- char *src;
- FOURCC hdr[2];
- int stream;
-
- // read next header
-
- left = 8;
- while(left > 0) {
- actual = left;
- src = _StreamRead(actual);
-
- if (!src)
- return read_something;
-
- memcpy((char *)hdr + (8-left), src, actual);
- left -= actual;
- }
-
- stream = StreamFromFOURCC(hdr[0]);
-
- if (isxdigit(hdr[0]&0xff) && isxdigit((hdr[0]>>8)&0xff) && stream<32 &&
- ((1L<<stream) & fStreamsActive)) {
-
- // _RPT3(0,"\tstream: reading chunk at %I64x, length %6ld, stream %ld\n", i64StreamPosition+sbPosition-8, hdr[1], stream);
-
- AVIStreamNode *pasn, *pasn_next;
- int streamno = 0;
-
- pasn = listStreams.AtHead();
-
- while(pasn_next = pasn->NextFromHead()) {
- if (streamno == stream) {
- long left = hdr[1] + (hdr[1]&1);
- bool fWrite = pasn->cache->WriteBegin(i64StreamPosition + sbPosition, left);
- char *dst;
-
- while(left > 0) {
- actual = left;
-
- dst = _StreamRead(actual);
-
- if (!dst) {
- if (fWrite)
- pasn->cache->WriteEnd();
- return read_something;
- }
-
- if (fWrite)
- pasn->cache->Write(dst, actual);
-
- left -= actual;
- }
-
- if (fWrite)
- pasn->cache->WriteEnd();
-
- read_something = true;
-
- break;
- }
-
- pasn = pasn_next;
- ++streamno;
- }
- } else {
-
- if (hdr[0] != FOURCC_LIST && hdr[0] != FOURCC_RIFF) {
- long actual;
-
- // skip chunk
-
- left = hdr[1] + (hdr[1] & 1);
-
- while(left > 0) {
- actual = left;
-
- if (!_StreamRead(actual))
- return read_something;
-
- left -= actual;
- }
- } else {
- left = 4;
-
- while(left > 0) {
- actual = left;
-
- if (!_StreamRead(actual))
- return read_something;
-
- left -= actual;
- }
- }
-
- }
- }
-
- return true;
- }
-
- __int64 AVIReadHandler::getStreamPtr() {
- return i64StreamPosition + sbPosition;
- }
-
- void AVIReadHandler::FixCacheProblems(AVIReadStream *arse) {
- AVIStreamNode *pasn, *pasn_next;
-
- // The simplest fix is simply to disable caching on the stream that's
- // cache-missing. However, this is a bad idea if the problem is a low-bandwidth
- // audio stream that's outrunning a high-bandwidth video stream behind it.
- // Check the streaming leader, and if the streaming leader is comparatively
- // low bandwidth and running at least 512K ahead of the cache-missing stream,
- // disable its cache.
-
- AVIStreamNode *stream_leader = NULL;
- int stream_leader_no;
- int streamno=0;
-
- pasn = listStreams.AtHead();
-
- while(pasn_next = pasn->NextFromHead()) {
- if (pasn->cache)
- if (!stream_leader || pasn->stream_pushes > stream_leader->stream_pushes) {
- stream_leader = pasn;
- stream_leader_no = streamno;
- }
-
- pasn = pasn_next;
- ++streamno;
- }
-
- if (stream_leader && stream_leader->stream_bytes*2 < arse->psnData->stream_bytes
- && stream_leader->stream_push_pos >= arse->psnData->stream_push_pos+524288) {
- #ifdef STREAMING_DEBUG
- OutputDebugString("caching disabled on fast puny leader\n");
- #endif
- delete stream_leader->cache;
- stream_leader->cache = NULL;
-
- DisableStreaming(stream_leader_no);
-
- i64StreamPosition = -1;
- sbPosition = sbSize = 0;
- } else {
- #ifdef STREAMING_DEBUG
- OutputDebugString("disabling caching at request of client\n");
- #endif
-
- arse->EndStreaming();
-
- if (arse->psnData == stream_leader) {
- i64StreamPosition = -1;
- sbPosition = sbSize = 0;
- }
- }
-
- // Reset cache statistics on all caches.
-
- pasn = listStreams.AtHead();
-
- while(pasn_next = pasn->NextFromHead()) {
- if (pasn->cache)
- pasn->cache->ResetStatistics();
-
- pasn = pasn_next;
- }
- }
-
- long AVIReadHandler::ReadData(int stream, void *buffer, __int64 position, long len) {
- if (nCurrentFile<0 || nCurrentFile != (int)(position>>48))
- _SelectFile((int)(position>>48));
-
- // _RPT3(0,"Reading from file %d, position %I64x, size %d\n", nCurrentFile, position, len);
-
- if (!_seekFile2(position & 0x0000FFFFFFFFFFFFi64))
- return -1;
- return _readFile(buffer, len);
- }
-
- void AVIReadHandler::_SelectFile(int file) {
- AVIFileDesc *pDesc, *pDesc_next;
-
- nCurrentFile = file;
-
- pDesc = listFiles.AtHead();
- while((pDesc_next = pDesc->NextFromHead()) && file--)
- pDesc = pDesc_next;
-
- hFile = pDesc->hFile;
- hFileUnbuffered = pDesc->hFileUnbuffered;
- i64Size = pDesc->i64Size;
- }
-